#include "include/joystick.h"

void *threadFunc(void *input)
{
    Joystick *joystick = (Joystick *)input;

    joystick->update();

    return NULL;
}

Joystick::Joystick(const char *device)
{
    this->device = new char[strlen(device) + 1];
    strcpy(this->device, device);

    fd = open(device, O_RDONLY);
    if (fd == -1)
    {
        perror("Failed to open joystick input.\n");
        exit(1);
    }

    name = new char[NAME_LENGTH];
    axmap = new uint8_t[ABS_MAX + 1];
    btnmap = new uint16_t[KEY_MAX - BTN_MISC + 1];

    ioctl(fd, JSIOCGVERSION, &version);
    ioctl(fd, JSIOCGAXES, &numAxes);
    ioctl(fd, JSIOCGBUTTONS, &numButtons);
    ioctl(fd, JSIOCGNAME(NAME_LENGTH), name);
    ioctl(fd, JSIOCGAXMAP, axmap);
    ioctl(fd, JSIOCGBTNMAP, btnmap);

    axis = new int[numAxes];
    button = new char[numButtons];

    pthread_create(&thread, NULL, threadFunc, this);
}

Joystick::~Joystick()
{
    pthread_cancel(thread);
    pthread_join(thread, NULL);

    delete []device;
    delete []name;
    delete []axmap;
    delete []btnmap;
    delete []axis;
    delete []button;
}

void Joystick::update()
{
    js_event js;

    while(true)
    {
        if (read(fd, &js, sizeof(js_event)) != sizeof(js_event))
        {
            perror("Error reading joystick input.\n");
            exit(1);
        }

        switch(js.type & ~JS_EVENT_INIT)
        {
            case JS_EVENT_BUTTON:
                button[js.number] = js.value;
                break;
            case JS_EVENT_AXIS:
                axis[js.number] = js.value;
                break;
        }
    }
}

char *Joystick::getDevice()
{
    char *copy = new char[strlen(device) + 1];
    strcpy(copy, name);
    return copy;
}

void Joystick::getDevice(char *device)
{
    strcpy(device, this->device);
}

unsigned char Joystick::getNumAxes()
{
    return numAxes;
}

unsigned char Joystick::getNumButtons()
{
    return numButtons;
}

int Joystick::getVersion()
{
    return version;
}

char *Joystick::getName()
{
    char *name = new char[strlen(this->name) + 1];
    strcpy(name, this->name);
    return name;
}

void Joystick::getName(char *name)
{
    strcpy(name, this->name);
}

uint8_t *Joystick::getAxmap()
{
    uint8_t *axmap = new uint8_t[ABS_MAX + 1];
    memcpy(axmap, this->axmap, sizeof(uint8_t)*(ABS_MAX + 1));
    return axmap;
}

void Joystick::getAxmap(uint8_t *axmap)
{
    memcpy(axmap, this->axmap, sizeof(uint8_t)*(ABS_MAX + 1));
}

uint16_t *Joystick::getBtnmap()
{
    uint16_t *btnmap = new uint16_t[KEY_MAX - BTN_MISC + 1];
    memcpy(btnmap, this->btnmap, sizeof(uint16_t)*(KEY_MAX - BTN_MISC + 1));
    return btnmap;
}

void Joystick::getBtnmap(uint16_t *btnmap)
{
    memcpy(btnmap, this->btnmap, sizeof(uint16_t)*(KEY_MAX - BTN_MISC + 1));
}

int *Joystick::getAxis()
{
    int *axis = new int[numAxes];
    memcpy(axis, this->axis, sizeof(int)*numAxes);
    return axis;
}

void Joystick::getAxis(int *axis)
{
    memcpy(axis, this->axis, sizeof(int)*numAxes);
}

int Joystick::getAxis(int index)
{
    return axis[index];
}

char *Joystick::getButton()
{
    char *button = new char[numButtons];
    memcpy(button, this->button, sizeof(char)*numButtons);
    return button;
}

void Joystick::getButton(char *button)
{
    memcpy(button, this->button, sizeof(char)*numButtons);
}

char Joystick::getButton(int index)
{
    return button[index];
}

char const *const Joystick::axis_name[ABS_MAX + 1] = {
"X", "Y", "Z", "Rx", "Ry", "Rz", "Throttle", "Rudder", 
"Wheel", "Gas", "Brake", "?", "?", "?", "?", "?",
"Hat0X", "Hat0Y", "Hat1X", "Hat1Y", "Hat2X", "Hat2Y", "Hat3X", "Hat3Y",
"?", "?", "?", "?", "?", "?", "?", 
};

char const *const Joystick::button_name[KEY_MAX - BTN_MISC + 1] = {
"Btn0", "Btn1", "Btn2", "Btn3", "Btn4", "Btn5", "Btn6", "Btn7", "Btn8", "Btn9", "?", "?", "?", "?", "?", "?",
"LeftBtn", "RightBtn", "MiddleBtn", "SideBtn", "ExtraBtn", "ForwardBtn", "BackBtn", "TaskBtn", "?", "?", "?", "?", "?", "?", "?", "?",
"Trigger", "ThumbBtn", "ThumbBtn2", "TopBtn", "TopBtn2", "PinkieBtn", "BaseBtn", "BaseBtn2", "BaseBtn3", "BaseBtn4", "BaseBtn5", "BaseBtn6", "BtnDead",
"BtnA", "BtnB", "BtnC", "BtnX", "BtnY", "BtnZ", "BtnTL", "BtnTR", "BtnTL2", "BtnTR2", "BtnSelect", "BtnStart", "BtnMode", "BtnThumbL", "BtnThumbR", "?",
"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", 
"WheelBtn", "Gear up",
};

